/**
 * 1. 编译模板，处理文件节点（插值表达式）和元素节点（指令）
 * 2. 遇到模板依赖数据，添加 watcher，数据改变更新视图
 */
class Compiler {constructor(vm) {
        this.vm = vm;
        this.el = vm.$el;
        this.compile(this.el);
    }
    compile(el) {
        let childNodes = el.childNodes;
        Array.from(childNodes).forEach(node => {
            if(this.isTextNode(node)) {
                this.compileText(node);
            }else if(this.isElementNode(node)) {
                this.compileElement(node);
            }
            if(node.childNodes && node.childNodes.length) {
                this.compile(node);
            }
        })
    }
    isTextNode(node) {
        return node.nodeType === 3
    }
    isElementNode(node) {
        return node.nodeType === 1
    }
    compileText(node) {
        let reg = /\{\{(.+)\}\}/;
        let value = node.textContent;
        if(reg.test(value)) {
            let key = RegExp.$1.trim();
            console.log("compileText -> key", key)
            node.textContent = value.replace(reg, this.vm[key]);

            new Watcher(this.vm, key, newValue => {
                node.textContent = newValue;
            })
        }
    }
    compileElement(node) {
        Array.from(node.attributes).forEach(attr => {
            let attrName = attr.name;
            if(this.isDirective(attrName)) {
                attrName = attrName.substr(2);
                if(attrName.includes('on:')) {
                    const tmp = attrName.split(':');
                    const name = tmp[1];
                    this.onHandler(node, attr.value, name)
                }else{
                    this.update(node, attr.value, attrName);
                }
            }
        })
    }
    isDirective(attrName) {
        return attrName.startsWith('v-');
    }
    update(node, key, attrName) {
        let fn = this[attrName + 'Updater']
        fn && fn.call(this, node, this.vm[key], key);
    }
    textUpdater(node, value, key) {
        node.textContent = value;
        new Watcher(this.vm, key, newValue => {
            node.textContent = newValue;
        })
    }
    modelUpdater(node, value, key) {
        node.value = value;
        new Watcher(this.vm, key, newValue => {
            node.value = newValue;
        })
        node.addEventListener('input', () => {
            this.vm[key] = node.value;
        })
    }
    htmlUpdater(node, value, key) {
        node.innerHTML = value;
        new Watcher(this.vm, key, newValue => {
            node.innerHTML = newValue;
        })
    }
    onHandler(node, value, name) {
        let modifier = '';
        if(name.includes('.')) {
            const tmp = name.split('.');
            name = tmp[0];
            modifier = tmp[1].trim();
        }
        
        // 动态时间处理：v-on:[event]="doThis"
        if(name.startsWith('[')) {
            name = name.slice(1, -1)
            name = this.vm[name]
        }
        
        let third_params = false;
        if(modifier === 'capture') {
            third_params = true
        }else if(modifier === 'passive') {
            third_params = { passive: true }
        }

        const cb = (e) => {
            if(modifier === 'stop') {
                e.stopPropagation();
            }
            if(modifier === 'prevent') {
                e.preventDefault();
            }
            let methodName = value;
            let args = [e];
            // 处理内联语句 传递额外参数
            if(value.endsWith(')')) {
                const tmp = value.split('(')
                methodName = tmp[0]
                args = tmp[1].slice(0,-1).split(',').map(item => {
                    item = item.trim();
                    console.log("onHandler -> item", item, typeof item)
                    if(item === '$event') {
                        return e;
                    }
                    if(item.startsWith('\"') || item.startsWith('\'')) {
                        console.log("onHandler -> item", item)
                        return item.slice(1, -1);
                    }
                    return this.vm[item];
                })
            }
            this.vm[methodName](...args)
            if(modifier === 'once') {
                node.removeEventListener(name, cb, third_params)
            }
        }
        node.addEventListener(name, cb, third_params)
    }
}